/*
 *   Copyright 1999-2018 dragonshard.net.
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *        http://www.apache.org/licenses/LICENSE-2.0
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package net.dragonshard.dsf.dynamic.datasource.aop;

import java.lang.reflect.Method;
import lombok.Setter;
import net.dragonshard.dsf.dynamic.datasource.DynamicDataSourceClassResolver;
import net.dragonshard.dsf.dynamic.datasource.annotation.DsfAssignDataSource;
import net.dragonshard.dsf.dynamic.datasource.processor.DsProcessor;
import net.dragonshard.dsf.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotationUtils;

/**
 * 动态数据源AOP核心拦截器
 *
 * @author TaoYu
 * @since 1.2.0
 */
public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor {

  /**
   * SPEL参数标识
   */
  private static final String DYNAMIC_PREFIX = "#";

  @Setter
  private DsProcessor dsProcessor;

  private DynamicDataSourceClassResolver dynamicDataSourceClassResolver = new DynamicDataSourceClassResolver();

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    try {
      DynamicDataSourceContextHolder.push(determineDatasource(invocation));
      return invocation.proceed();
    } finally {
      DynamicDataSourceContextHolder.poll();
    }
  }

  private String determineDatasource(MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    Class<?> declaringClass = dynamicDataSourceClassResolver.targetClass(invocation);
    DsfAssignDataSource ds = method.isAnnotationPresent(DsfAssignDataSource.class) ? method
      .getAnnotation(DsfAssignDataSource.class)
      : AnnotationUtils.findAnnotation(declaringClass, DsfAssignDataSource.class);
    String key = ds.value();
    return (!key.isEmpty() && key.startsWith(DYNAMIC_PREFIX)) ? dsProcessor
      .determineDatasource(invocation, key) : key;
  }
}
